#include <sys/types.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "btree.h"
#include "config.h"

int g_btree_constmax = 0 ;

uint32_t COP(char** exp, char* end, uint32_t* in, int inmax, int level, bool print)
{
//    printf("COP %d\n", (int) (end - *exp));
    bool ret = false;

    unsigned char op = (unsigned char) **exp % op_num;

    if (print)
    {
        TABS(level);
        printf("%s\n", ops[op]);
    }

    (*exp)++;

    uint32_t op1 = TOP(exp, end, in, inmax, level+1, print);
    uint32_t op2 = TOP(exp, end, in, inmax, level+1, print);

//    printf("%d %d\n", op1, op2);

    switch(op)
    {
        case COP_G:
                    ret = op1 > op2;
                    break;
        case COP_GE:
                    ret = op1 >= op2;
                    break;
        case COP_L:
                    ret = op1 < op2;
                    break;
        case COP_LE:
                    ret = op1 <= op2;
                    break;
        case COP_EQ:
                    ret = op1 == op2;
                    break;
    }   

    return ret ? 1 : 0; 
}

uint32_t TOP(char** exp, char* end, uint32_t* in, int inmax, int level, bool print)
{
//    log_add("TOP %d\n", (int) (end - *exp));
    uint32_t ret = 0;
    if (*exp + TOP_LIMIT > end)
    {
//        printf("TOP: returning 0");
        return ret;
    }

    unsigned char op =(unsigned char) **exp % am_num;

    (*exp)++;                  // move to the next byte

    if (print)
    {
        TABS(level);
        printf("%s", ams[op]);
    }

    switch(op)
    {
        case AM_CONST: 
                    ret = *((uint32_t*) *exp);       // dereference the float starting here
                    ret = ret % (g_btree_constmax + 1);
                    break;
        case AM_INPUT:
                    uint32_t idx = (**exp << 8) + *((*exp)+1);
                    ret = in[idx % inmax];

                    if (print)
                    {
                        printf("[%d]", idx%inmax);
                    }

                    break;
    }

    (*exp) += sizeof(uint32_t);               // move past the two bytes

    if (print)
    {
        printf(":%d\n", ret);
    }

    return ret;
} 

uint32_t BEXP(char** exp, char* end, uint32_t* in, int inmax, int level, bool print)
{
    uint32_t ret = 0.0f;
    if ((long)*exp + BEXP_LIMIT > (long)end)
    {
//        log_add("BEX:returning 0\n");
        return (uint32_t) ret;
    }
   
    unsigned char op = (unsigned char) **exp % op_num;

    if (op < bop_num)
    {
        if (print)
        {
            TABS(level); 
            printf("%s\n", ops[op]);
        }

        (*exp)++;
        uint32_t op1 = BEXP(exp, end, in, inmax, level+1, print); 
        uint32_t op2 = BEXP(exp, end, in, inmax, level+1, print);
//        log_add("op1:%s op2:%s", op1 ? "true" : "false", op2 ? "true" : "false");

        switch (op)
        {
            case BOP_AND:   return (op1 && op2) ? 1 : 0;
                            break;
            case BOP_OR:    return (op1 || op2) ? 1 : 0;
                            break;
        }
        
    } else 
    {
        return COP(exp, end, in, inmax, level, print);
    }
}



void btree_prg_print(char** exp, char* end)
{
    for(char* tmp = *exp ; tmp  < end ; tmp++)
    {
        char ch = *tmp;
        printf("%d ", *tmp);
    }
    printf("\n");
}

void btree_init(int constmax)
{
    srand(time(NULL));
    g_btree_constmax = constmax;
}

bool btree_eval(char** exp, char* end, uint32_t* in)
{
    return (bool) BEXP(exp, end, in, sizeof(in)/sizeof(uint32_t), 0, false);
}

bool btree_eval(char** exp, int len, uint32_t* in)
{
    char* expstart = *exp;
    return btree_eval(&expstart, expstart + len, in);
}

bool btree_print(char** exp, int len, uint32_t* in)
{
    char* expstart = *exp;
    return (bool)BEXP(&expstart, expstart+len, in, sizeof(in)/sizeof(float), 0, true);
}

bool btree_print(char** exp, char* end, uint32_t* in)
{
    char* expstart = *exp;
    return (bool)BEXP(&expstart, end, in, sizeof(in)/sizeof(float), 0, true);
}

void btree_save(const char* path, char* exp, int length)
{
    FILE* f = fopen(path, "wt");

    fprintf(f, "%d\n", length);

    for(int i = 0 ; i < length; i++)
    {
				char val =  exp[i];
        fprintf(f, "%d ", val);
    }
    
    fclose(f);

//    printf("btree saved to %s\n", path);
}

// returns length
int btree_load(const char* path, char** exp)
{
    FILE* f = fopen(path, "rt");

    if (f == NULL)  
    {
        return -1;
    }

    int length = -1;
    
    fscanf(f, "%d\n", &length );

    *exp = new char[length];

    for(int i = 0 ; i < length; i++)
    {
        int ch;
        fscanf(f, "%d ", &ch );
        (*exp)[i]=ch;
    }

    fclose(f);

    printf("btree loaded from %s\n", path);

    return length;
}

void btree_randomise(char** exp, int length)
{
    char* exps = *exp;
    char* end = &exps[length];

    for(char* t = exps; t!=end; t++)
    {
      *t=rand() % 256;
    }
}

void btree_mutate(char** exp, int length)
{
    char* exps = *exp;
    char* end = &exps[length];

    int ofs = rand() % ((long)end - (long)exps);
   
    *(*exp + ofs) += rand() % 256;

}
